//
//  StrokeRecordLoader.m
//  StrokeDemo
//
//  Created by wankang on 2/16/15.
//  Copyright (c) 2015 wankang. All rights reserved.
//

#import "StrokeRecordLoader.h"
#import "StrokeRecord.h"

/////////////////////////////////////////////////////////////

static char TABLE_STROKE[] = "tb_stroke";
static char SELECT_REC_SQL[] = "SELECT code,strokeindex,strokename,x,y,type,endAngle FROM %s WHERE %s;";
static char SELECT_STROKE[] = "SELECT DISTINCT strokename from %s;";
static char BINARY_CONDITION[] = "(code='%s') AND (strokeindex=%d)";
static char SELECT_BINARY[] = "SELECT %s FROM %s WHERE %s;";

static char SELECT_GUIDE[] = "code='%s' AND idx=%d";

#ifndef _countof
#define _countof(A) (sizeof(A)/sizeof(A[0]))
#endif  // _countof

/////////////////////////////////////////////////////////////

@implementation StrokeRecordLoader
@synthesize loadPath = _loadPath;
@synthesize guidePath = _guidePath;

-(id)initWithPath:(NSString *)sPath
{
    if (self = [super init]) {
        _loadPath = sPath;
        _guidePath = nil;
    }
    return self;
}

-(void)dealloc
{
    [self close];
}

-(void)close
{
    _loadPath = nil;
    _guidePath = nil;
    [self destroyStrokeDB];
    [self destroyGuideDB];
}

-(NSArray *)loadRecordFromCode:(NSString *)sCode withContour:(BOOL)fContour withMidline:(BOOL)fMidline
{
    if (sCode==nil || sCode.length!=1)
        return nil;
    char sCondition[20];
    sprintf(sCondition, "code='%s'", sCode.UTF8String);
    return [self loadRecordWithCondition:sCondition withContour:fContour withMidline:fMidline];
}

-(NSArray *)loadRecordWithCondition:(const char *)sCondition withContour:(BOOL)fContour withMidline:(BOOL)fMidline
{
    char *pszMsg, szSql[200];
    char **ppResult;
    int nRow, nCol, i;
    if (!sCondition || !*sCondition)
        return nil;
    if (!mStrokeDB && ![self initStrokeDB])
        return nil;
    char *pSql = szSql;
    int nMaxLength = (int)strlen(SELECT_REC_SQL) + (int)strlen(TABLE_STROKE) + (int)strlen(sCondition) + 1;
    if (nMaxLength > 200)
        pSql = (char *)malloc(nMaxLength);
    sprintf(pSql, SELECT_REC_SQL, TABLE_STROKE, sCondition);
    int nRes = sqlite3_get_table(mStrokeDB, pSql, &ppResult, &nRow, &nCol, &pszMsg);
    if (pSql != szSql)
        free(pSql);
    if (nRes != SQLITE_OK)
        return nil;
    if (nRow <= 0) {
        sqlite3_free_table(ppResult);
        return nil;
    }
    int nBase = nCol;
    NSMutableArray *sRecord = [[NSMutableArray alloc] initWithCapacity:nRow];
    for(i=0; i<nRow; i++) {
        StrokeRecord *rec = [[StrokeRecord alloc] init];
        rec.code = [[NSString alloc] initWithUTF8String:ppResult[nBase + 0]];
        rec.indexContour = atoi(ppResult[nBase + 1]);
        rec.nameStroke = [[NSString alloc] initWithUTF8String:ppResult[nBase + 2]];
        rec.xStart = atoi(ppResult[nBase + 3]);
        rec.yStart = atoi(ppResult[nBase + 4]);
        rec.type = atoi(ppResult[nBase + 5]);
        rec.endAngle = atoi(ppResult[nBase + 6]);
        [sRecord addObject:rec];
        nBase += nCol;
    }
    sqlite3_free_table(ppResult);
    // to query binary
    if (fContour || fMidline) {
        for(i=0; i<nRow; i++) {
            StrokeRecord *rec = (StrokeRecord *)[sRecord objectAtIndex:i];
            if (rec.code.length != 1)
                continue;
            sprintf(szSql, BINARY_CONDITION, rec.code.UTF8String, (int)rec.indexContour);
            if (fContour) {
                rec.arrContour = [self savePointFromField:"outline" withCondition:szSql];
            }
            if (fMidline) {
                rec.arrMiddle = [self savePointFromField:"midline" withCondition:szSql];
            }
        }
    }
    return sRecord;
}

-(NSArray *)loadStrokeName
{
    char **ppResult, szBuf[100];
    int nRow, nCol, i;
    if (!mStrokeDB && ![self initStrokeDB])
        return nil;
    sprintf(szBuf, SELECT_STROKE, TABLE_STROKE);
    if (sqlite3_get_table(mStrokeDB, szBuf, &ppResult, &nRow, &nCol, NULL) != SQLITE_OK)
        return nil;
    NSMutableArray *sNameList = [[NSMutableArray alloc] initWithCapacity:nRow];
    for (i=0; i<nRow; i++) {
        NSString *s = [[NSString alloc] initWithUTF8String:ppResult[i+1]];
        [sNameList addObject:s];
    }
    sqlite3_free_table(ppResult);
    return sNameList;
}

-(NSArray *)getChnGuide:(NSString *)sChn withStrokeIndex:(NSInteger)index
{
    if (_guidePath==nil || _guidePath.length==0 ||
        sChn==nil || sChn.length!=1 ||
        index<0 || index>=64)
        return nil;
    if (!mGuideDB && ![self initGuideDB])
        return nil;
    char szBuf[100], szCond[40];
    sprintf(szCond, SELECT_GUIDE, sChn.UTF8String, (int)index+1);
    sprintf(szBuf, SELECT_BINARY, "data", "tb_guide", szCond);
    unsigned char *pbData;
    int nRes = [StrokeRecordLoader getBinary:&pbData fromSql:szBuf inDB:mGuideDB];
    NSArray *res = [StrokeRecordLoader savePointByte:pbData bytes:nRes];
    free(pbData);
    return res;
}

-(void)setLoadPath:(NSString *)loadPath
{
    [self destroyStrokeDB];
    _loadPath = loadPath;
}

-(void)setGuidePath:(NSString *)guidePath
{
    [self destroyGuideDB];
    _guidePath = guidePath;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

-(NSArray *)savePointFromField:(const char *)sField withCondition:(const char *)sCondition
{
    int nBytes;
    unsigned char *pbData;
    nBytes = [self getBinary:&pbData fromTable:TABLE_STROKE inField:sField where:sCondition];
    NSArray *sResult = [StrokeRecordLoader savePointByte:pbData bytes:nBytes];
    free(pbData);
    return sResult;
}

+(NSArray *)savePointByte:(const unsigned char *)pbData bytes:(int)nBytes
{
    if (!pbData || nBytes<=0 || (nBytes%4)!=0) {
        return nil;
    }
    NSNumber *num;
    int nPt = (nBytes / 4);
    NSMutableArray *sPoint = [[NSMutableArray alloc] initWithCapacity:nPt];
    for(int i=0; i<nPt; i++) {
        const int *pnData = (const int *)(pbData + i * 4);
        num = [[NSNumber alloc] initWithInteger:*pnData];
        [sPoint addObject:num];
    }
    return sPoint;
}

-(int)getBinary:(unsigned char **)ppbData fromTable:(const char *)szTable inField:(const char *)szField where:(const char *)szWhere
{
    char szBuf[200];
    char *pBuf = szBuf;
    int nRes, nBytes;
    *ppbData = NULL;
    nBytes = (int)strlen(szTable) + (int)strlen(szField) + (int)strlen(szWhere);
    if (_countof(SELECT_BINARY) + nBytes > 200) {
        if ((pBuf = malloc(_countof(SELECT_BINARY) + nBytes)) == NULL)
            return -1;
    }
    sprintf(pBuf, SELECT_BINARY, szField, szTable, szWhere);
    nRes = [StrokeRecordLoader getBinary:ppbData fromSql:pBuf inDB:mStrokeDB];
    if (pBuf != szBuf)
        free(pBuf);
    return nRes;
}

+(int)getBinary:(unsigned char **)ppbData fromSql:(const char *)szSql inDB:(sqlite3 *)db
{
    int nRes = 0;
    sqlite3_stmt *pstmt;
    *ppbData = NULL;
    if (sqlite3_prepare(db, szSql, -1, &pstmt, NULL) != SQLITE_OK)
        return 0;
    if ((nRes=sqlite3_step(pstmt)) == SQLITE_ROW) {
        const void *p = sqlite3_column_blob(pstmt, 0);
        if ((nRes = sqlite3_column_bytes(pstmt, 0)) > 0) {
            unsigned char *pbData = (unsigned char *)malloc(nRes);
            memcpy(pbData, p, nRes);
            *ppbData = pbData;
        }
    } else {
        NSLog(@"no data: sqlite3_step=%d", nRes);
    }
    sqlite3_finalize(pstmt);
    return nRes;
}

-(BOOL)initStrokeDB
{
    if (mStrokeDB != NULL)
        return YES;
    if (_loadPath == nil || _loadPath.length == 0)
        return NO;
    if (sqlite3_open(_loadPath.UTF8String, &mStrokeDB) == SQLITE_OK)
        return YES;
    return NO;
}

-(void)destroyStrokeDB
{
    if (mStrokeDB != NULL) {
        sqlite3_close(mStrokeDB);
        mStrokeDB = NULL;
    }
}

-(BOOL)initGuideDB
{
    if (mGuideDB != NULL)
        return YES;
    if (_guidePath == nil || _guidePath.length == 0)
        return NO;
    if (sqlite3_open(_guidePath.UTF8String, &mGuideDB) == SQLITE_OK)
        return YES;
    return NO;
}

-(void)destroyGuideDB
{
    if (mGuideDB != NULL) {
        sqlite3_close(mGuideDB);
        mGuideDB = NULL;
    }
}
@end
